#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

int set_non_blocking(int fd) {
	int flags = fcntl(fd, F_GETFL, 0);
	flags |= O_NONBLOCK;
	fcntl(fd, F_SETFL, flags);
	return fd;
}

int main() {
	int listen_fd;
	struct sockaddr_in server_addr;
	socklen_t server_addr_len;

	int client_fd;
	struct sockaddr_in client_addr;
	socklen_t client_addr_len;

	int ep_fd;
	struct epoll_event ev;
	struct epoll_event *ready_evs;
	const int MAX_EPOLL_EVENTS = 100;

	memset(&server_addr, 0, sizeof(struct sockaddr_in));
	server_addr_len = sizeof(struct sockaddr_in);
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(7726);

	listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	set_non_blocking(listen_fd);
	bind(listen_fd, (const struct sockaddr *)&server_addr, server_addr_len);
	listen(listen_fd, 5);

	ready_evs = new struct epoll_event[MAX_EPOLL_EVENTS];
	ep_fd = epoll_create(MAX_EPOLL_EVENTS);
	ev.events = EPOLLIN | EPOLLOUT;
	ev.data.fd = listen_fd;
	epoll_ctl(ep_fd, EPOLL_CTL_ADD, listen_fd, &ev);
	
	while (true) {
		int nfds = epoll_wait(ep_fd, ready_evs, MAX_EPOLL_EVENTS, -1);
		printf("epoll_wait return %d\n", nfds);
		for (int i = 0; i < nfds; i++) {
			int fd = ready_evs[i].data.fd;
			if (listen_fd == fd) {
				if (ready_evs[i].events & EPOLLIN) {
					printf("listen_fd EPOLLIN return");
					while (true) {
						memset(&client_addr, 0, sizeof(struct sockaddr_in));
						client_addr_len = sizeof(struct sockaddr_in);
						client_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_len);
						if (client_fd == -1) break;
						set_non_blocking(client_fd);
						ev.events = EPOLLIN | EPOLLOUT;
						ev.data.fd = client_fd;
						epoll_ctl(ep_fd, EPOLL_CTL_ADD, client_fd, &ev);
					}
				}
				if (ready_evs[i].events & EPOLLOUT) {
					printf("listen_fd EPOLLOUT return");
				}
			}
			else {
				if (ready_evs[i].events & EPOLLIN) {
					printf("client_fd EPOLLIN return\n");
					char buff[1010];
					size_t n = read(client_fd, buff, 1010);
					write(1, buff, n);
				}
				if (ready_evs[i].events & EPOLLOUT) {
					printf("client_fd EPOLLOUT return\n");
				}
			}
		}
	}
}
